package com.qust.enDecrpytion.proxy.builder;

import com.alibaba.fastjson2.JSONArray;
import com.qust.agreement.result.Result;
import com.qust.enDecrpytion.annotations.EnDecryptionConfiguration;
import com.qust.enDecrpytion.encryptionAndDecryption.remote.RemoteEnDecryption;
import com.qust.enDecrpytion.looPool.ChannelLooPool;
import com.qust.enDecrpytion.proxy.decryption.DecryptionProxy;
import com.qust.enDecrpytion.proxy.status.AbstractTypeHandle;
import com.qust.enDecrpytion.util.net.HttpMethod;
import com.qust.enDecrpytion.util.net.NetTool;
import com.qust.encryptionAndDecryption.IEnDecryption;
import com.qust.encryptionAndDecryption.KeyPair;
import com.qust.encryptionAndDecryption.enDecryption.AbstractEnDecryptionWithKeyPair;
import com.qust.util.NettyUtils;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import lombok.extern.log4j.Log4j2;
import net.sf.cglib.proxy.Enhancer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@Log4j2
public class EnDecryptionProxyBuilder {
    public static int numberOfCores = 3;
    public static int maximumNumber = 10;
    public static  EventLoopGroup EVENT_LOOP_GROUP;
    private static ChannelLooPool channelLooPool;

    public static <T> T proxy(T target, IEnDecryption enDecryption, Class<? extends AbstractTypeHandle>[] handlers) {
        DecryptionProxy decryptionProxy = new DecryptionProxy(target, enDecryption);
        decryptionProxy.ConfigContext(handlers);
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(decryptionProxy);
        enhancer.setSuperclass(target.getClass());
        return (T) enhancer.create();
    }


    public static <T> T proxy(T target) {
        // 1. 判断是否被@EnDecryptionConfiguration标识
        EnDecryptionConfiguration enDecryptionConfiguration = target.getClass().getAnnotation(EnDecryptionConfiguration.class);
        if (enDecryptionConfiguration != null){
            EnDecryptionConfiguration.Remote remote;
            EnDecryptionConfiguration.Local local;
            Class<? extends AbstractTypeHandle>[] classes = enDecryptionConfiguration.customHandlers();

            if (enDecryptionConfiguration.remote().length == 1){
                remote = enDecryptionConfiguration.remote()[0];
                return remoteParse(target,remote,classes);
            }else if (enDecryptionConfiguration.local().length == 1){
                local = enDecryptionConfiguration.local()[0];
                return localParse(target,local,classes);
            }else {
                return target;
            }
        }else {
            return target;
        }
    }

    private static <T> T remoteParse(T target, EnDecryptionConfiguration.Remote remote,Class<? extends AbstractTypeHandle>[] handlers){
        EVENT_LOOP_GROUP = new NioEventLoopGroup();
        channelLooPool = new ChannelLooPool(numberOfCores,maximumNumber);
        int algorithmCode = remote.algorithmCode();
        String managerAddress = remote.managerAddress();
        getConnectChannels(managerAddress);
        RemoteEnDecryption remoteEnDecryption = new RemoteEnDecryption(EVENT_LOOP_GROUP,algorithmCode,channelLooPool);
        return proxy(target,remoteEnDecryption,handlers);
    }

    private static void getConnectChannels(String managerAddress){
        String url = "http://"+managerAddress+"/LoadBalance/"+numberOfCores;
        NetTool netTool = new NetTool();
        Result<?> request = netTool.request(url, HttpMethod.GET);
        ArrayList<?> data =(JSONArray) request.getData();
        log.debug("创建连接池");
        for (Object datum : data) {
            String address = (String) datum;
            String ip = address.split(":")[0];
            int port = Integer.parseInt(address.split(":")[1]);
            NettyUtils.MessageHandle messageHandle = new NettyUtils.MessageHandle();
            Channel channel = NettyUtils.generateChannel(ip, port, EVENT_LOOP_GROUP, messageHandle);
            Map<Channel, NettyUtils.MessageHandle> map = new HashMap<>();
            map.put(channel,messageHandle);
            channelLooPool.addNode(map);
        }
        log.debug("连接完毕");
    }

    private static <T> T localParse(T target, EnDecryptionConfiguration.Local local,Class<? extends AbstractTypeHandle>[] handlers) {
        try {
            AbstractEnDecryptionWithKeyPair iEnDecryption =(AbstractEnDecryptionWithKeyPair) local.enDecryption().newInstance();
            KeyPair keyPair = new KeyPair();
            keyPair.loadKey(local.keyPath(),local.password());
            iEnDecryption.loadKeyPair(keyPair);
            return proxy(target,iEnDecryption,handlers);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void closeConnect() {
        log.info("等待eventLoop关闭.....");
        EVENT_LOOP_GROUP.shutdownGracefully();
    }

}
